package taller2.view.draws;

import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.QuadCurve2D;

import taller2.utils.Constants;
import taller2.utils.Point;

/**
 * not mine, all credit to:
 * http://research.cs.vt.edu/AVresearch/openalgoviz/JFLAP/trunk/gui/viewer/CurvedArrow.java
 * @author Thomas Finley
 */

public class CurvedArrow {
	private Point start, end, control;
	private Point high;
	protected float curvy;

	protected QuadCurve2D.Float curve;
	protected boolean needsRefresh = true;

	protected boolean startArrow = false, endArrow = false;
	protected String label = "";
	protected String flux = "";
	
	private boolean drawArrow;
	private static double ARROW_ANGLE = Math.PI / 10;
	private static double ARROW_LENGTH = 15;

	protected java.awt.geom.Rectangle2D bounds = new java.awt.Rectangle(0, 0);
	protected AffineTransform affineToText;

	private static final int CHARS_PER_STEP = 4;
	protected static Graphics GRAPHICS = null;
	protected static FontMetrics METRICS;	
	
	private static double HEIGHT = 30.0;
	
	public CurvedArrow(int x1, int y1, int x2, int y2, float curvy, String label, String flux) {
		curve = new QuadCurve2D.Float();
		start = new Point(x1, y1);
		end = new Point(x2, y2);
		drawArrow = true;
		
		this.label = label;
		this.flux = flux;
		this.curvy = curvy;
		refreshCurve();
	}

	public void draw(Graphics2D g) {
		if (needsRefresh)
			refreshCurve();
		g.draw(curve); 				// Draws the main part of the arrow.
	
		if(drawArrow)
			drawArrow(g, end, control); // Draws the arrow head.
	
		if(!label.matches(String.valueOf(Constants.ConnectionKilled)))
			drawText(g,label);

		if(!flux.matches(Constants.FluxKilled))
			drawText(g,flux);
	}

	public void dontDrawArrow(){
		drawArrow = false;
	}
	
	public void drawText(Graphics2D g,String text) {

		Graphics2D g2 = (Graphics2D) g.create();
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setColor(Constants.fontColor);
		g2.transform(affineToText);

		FontMetrics metrics = g2.getFontMetrics();
		bounds = metrics.getStringBounds(text, g2);

		boolean upsideDown = end.getX() < start.getX();
		float dx = (float) bounds.getWidth() / 2.0f;
		float dy = (curvy < 0.0f) ^ upsideDown ? metrics.getAscent() : -metrics.getDescent();
		bounds.setRect(bounds.getX() - dx, bounds.getY() + dy, bounds.getWidth(), bounds.getHeight());
		for (int i = 0; i < text.length(); i += CHARS_PER_STEP) {
			String sublabel = text.substring(i, Math.min(i + CHARS_PER_STEP,text.length()));
			g2.drawString(sublabel, -dx, dy);
			dx -= metrics.getStringBounds(sublabel, g2).getWidth();
		}
		g2.dispose();
	}

	private void drawArrow(Graphics g, Point head, Point away) {
		int endX, endY;
		double angle = Math.atan2((double) (away.getX() - head.getX()),(double) (away.getY() - head.getY()));
		angle += ARROW_ANGLE;
		endX = ((int) (Math.sin(angle) * ARROW_LENGTH)) + head.getX();
		endY = ((int) (Math.cos(angle) * ARROW_LENGTH)) + head.getY();
		g.drawLine(head.getX(), head.getY(), endX, endY);
		angle -= 2 * ARROW_ANGLE;
		endX = ((int) (Math.sin(angle) * ARROW_LENGTH)) + head.getX();
		endY = ((int) (Math.cos(angle) * ARROW_LENGTH)) + head.getY();
		g.drawLine(head.getX(), head.getY(), endX, endY);
	}

	public void refreshCurve() {
		needsRefresh = false;

        double lengthx = end.getX() - start.getX();
        double lengthy = end.getY() - start.getY();
        double centerx = ((double) (start.getX() + end.getX())) / 2.0;
        double centery = ((double) (start.getY() + end.getY())) / 2.0;

        double length = Math.sqrt(lengthx * lengthx + lengthy * lengthy);
        double factorx = length == 0.0 ? 0.0 : lengthx / length;
        double factory = length == 0.0 ? 0.0 : lengthy / length;
        
        control = new Point((int) (centerx + curvy * HEIGHT * factory),(int) (centery - curvy * HEIGHT * factorx));
        high = new Point((int) (centerx + curvy * HEIGHT * factory / 2.0),(int) (centery - curvy * HEIGHT * factorx / 2.0));

        curve.setCurve((float) start.getX(), (float) start.getY(), (float) control.getX(),(float) control.getY(), (float) end.getX(), (float) end.getY());

        affineToText = new AffineTransform();
        affineToText.translate(high.getX(), high.getY());
        affineToText.rotate(Math.atan2(lengthy, lengthx));
        
        if (end.getX() < start.getX())
        	affineToText.rotate(Math.PI);
	}
}